xui.Class('xui.ModuleFactory', null, {
    Initialize: function () {

        var ns = this;

        xui.getModule = function (cls, onEnd, threadid, cached, properties, events) {
            return ns.getModule.apply(ns, arguments)
        };

        xui.newModule = function (cls, onEnd, threadid, properties, events) {
            return ns.newModule.apply(ns, arguments)
        };
        xui.showModule = function (cls, beforeShow, onEnd, threadid, cached, properties, events, parent, subId, left, top) {
            return ns.getModule(cls, function (err, module, threadid) {
                if (!err && false !== xui.tryF(beforeShow, [module, threadid], module)) {
                    this.show.apply(module, [onEnd, parent, subId, threadid, left, top]);
                } else {
                    xui.tryF(onEnd, [err, module, threadid], module);
                }
            }, threadid, cached, properties, events);
        };
        xui.execExpression = function (expression, params, isasy) {
            var result;
            if (!params) {
                params = {};
            }
            params.expression = expression;
            xui.Ajax('/LOCALESD/executeCMD', params, function (txt) {
                var obj = txt;
                if (obj && obj.requestStatus != -1) {
                    result = obj.data;
                } else {
                    xui.message(xui.get(obj, ['error', 'errdes']) || obj || 'no response!');
                }

            }, null, null, {method: 'POST', asy: isasy ? true : false}).start();
            return result;
        }
        xui.execGrid = function (expression, params, isasy) {
            var result;
            if (!params) {
                params = {};
            }
            params.expression = expression;
            xui.Ajax('/LOCALESD/executeGridCMD', params, function (txt) {
                var obj = txt;
                if (obj && obj.requestStatus != -1) {
                    result = obj.data;
                } else {
                    xui.message(xui.get(obj, ['error', 'errdes']) || obj || 'no response!');
                }

            }, null, null, {method: 'POST', asy: isasy ? true : false}).start();
            return result;
        };

        xui.downLoad = function (path) {
            xui.create('xui.UI.Div')
                .setLeft('0')
                .setTop('0')
                .setWidth('0').setHeight('0')
                .setIframeAutoLoad('/vfs/download?path=' + path)
                .show()
        };

        xui.showModule3 = function (cls, target, childname, callback) {
            if (!cls) {
                return;
            }
            if (childname == '') {
                childname = undefined;
            }
            try {
                if (!xui.isStr(target)) {
                    target = target.getAlias();
                }
                var arr = xui.getObjectByAlias(target);
                currpanel = arr[arr.length - 1];
                var domId = currpanel.getDomId();
                xui.each(arr, function (ccurrpanel) {
                    if (ccurrpanel.getDomId() != domId) {
                        try {
                            ccurrpanel.boxing().destroy();
                            //ccurrpanel.boxing().dumpContainer();
                        } catch (e) {
                            console.log(e);
                        }

                    } else {
                        if (childname && childname != '') {
                            var childs = ccurrpanel.boxing().getPanelChildren();
                            xui.each(childs, function (child) {
                                    if (child[1] == childname) {
                                        var box = child[0].boxing().destroy();
                                    }
                                }
                            )
                            //  ccurrpanel.boxing().dumpContainer(childname)
                        }
                    }
                });

            } catch (e) {
                console.log(e);
            }

            var onEnd = function (url, remoteclass, test) {
                var clazz = xui.ModuleFactory.getModule(remoteclass, function () {
                    this.show(callback, currpanel, childname, true)
                })
            }
            var onFail = function (msg) {
                xui.alert(msg)
            }
            xui.fetchClass(cls, onEnd, onFail, null, false)
        };

        //xui.showModule = function (cls, beforeShow, onEnd, threadid, cached, properties, events, parent, subId, left, top) {
        xui.showModule2 = function (cls, target, childname, params, data, title, parentModule) {
            if (!cls) {
                return;
            }
            if (childname == '') {
                childname = undefined;
            }
            try {
                var currpanel;
                if (!xui.isStr(target)) {
                    target = target.getAlias();
                }
                if (parentModule) {
                    currpanel = xui.get(parentModule, [target]) || xui.get(parentModule, ["host", target]);
                }
                if (!currpanel) {
                    var arr = xui.getObjectByAlias(target);
                    currpanel = arr[arr.length - 1];
                }
                if (currpanel) {
                    if (childname) {
                        var childs = currpanel.children||currpanel.get(0).children;
                        xui.each(childs, function (child) {
                                if (child[1] == childname) {
                                    var box = child[0].boxing().destroy();
                                }
                            }
                        )
                    } else {
                        currpanel.boxing().destroy();
                    }

                } else if (!currpanel) {
                    var arr = xui.getObjectByAlias(target);
                    currpanel = arr[arr.length - 1];
                    var domId = currpanel.getDomId();
                    xui.each(arr, function (ccurrpanel) {
                        if (ccurrpanel.getDomId() != domId) {
                            try {
                                ccurrpanel.boxing().destroy();
                            } catch (e) {
                                console.log(e);
                            }
                        } else {
                            if (childname && childname != '') {
                                var childs = ccurrpanel.boxing().getPanelChildren();
                                xui.each(childs, function (child) {
                                        if (child[1] == childname) {
                                            var box = child[0].boxing().destroy();
                                        }
                                    }
                                )
                            }
                        }
                    });
                }


            } catch (e) {
                console.log(e);
            }


            var onEnd = function (url, remoteclass, test) {

                var endFun = function () {
                    var allData = {};
                    if (data && xui.isHash(data)) {
                        xui.merge(allData, data);
                    }
                    try {
                        if (params && xui.isHash(params)) {
                            xui.merge(params, this.properties,'with');
                            var urlParams = xui.getUrlParams(url);
                            if (urlParams) {
                                xui.merge(params, urlParams);
                            }
                            xui.each(allData, function (prf) {
                                if (prf.name && params[prf.name]) {
                                    prf.value = params[prf.name];
                                }
                            })
                            xui.merge(allData, params, true);
                        }

                        this.setData(allData)

                    } catch (e) {
                        console.log(e)
                    }
                    this.setProperties("target",target)
                    this.setProperties("childname",childname)
                    this.setName(childname);
                    this.initData();
                    this._fireEvent('afterShow');
                };

                var clazz = xui.ModuleFactory.newModule(remoteclass, function () {
                    if (parentModule) {
                        this.setParentModule(parentModule);
                    }

                    this.show(endFun, currpanel, childname, true)
                }, false, params)
            }
            var onFail = function (msg) {
                xui.alert(msg)
            }
            xui.fetchClass(cls, onEnd, onFail, null, false)
        };


        xui._addChildModule = function (citem, child, host, $host) {

            var cchild;
            if (citem.key == 'xui.Module' && citem.className) {
                cchild = xui.create(citem.className, citem.key)
                    .setHost(host, citem.alias)
                    .setAlias(citem.alias)
                    .setEvents(xui.checkEvents(citem.events))
                    .setProperties(citem.properties)
                if ($host) {
                    $host[citem.alias] = cchild
                }
                if (citem.CS) {
                    cchild.setCustomStyle(citem.CS)
                }
                if (child.append) {
                    var target = citem.target || citem.tagter;
                    if (target) {
                        child.append(cchild, target);
                    } else {
                        child.append(cchild);
                    }
                }
                //  try { cchild.initData(); } catch (e) { }

            }
        };

        xui.addChild = function (item, child, host, $host) {
            var children = item.children || item.sub, apis = [], customComponents = [], components = [], modules = [],
                ns = this;
            host.modules = [];
            xui.each(children, function (citem) {

                if (citem.key == 'xui.APICaller') {
                    var cchild = xui.create(citem.key)
                        .setHost($host, citem.alias)
                        .setAlias(citem.alias)
                        .setEvents(xui.checkEvents(citem.events))
                        .setProperties(citem.properties)
                    if ($host) {
                        $host[citem.alias] = cchild
                    }
                    ;
                    apis.push(cchild)
                } else if (xui.str.startWith(citem.key, 'xui.UI.')) {
                    customComponents.push(citem);
                } else {
                    if (!citem.alias) {
                        citem.alias = xui.id()
                    }
                    var cchild = xui.create(citem.key)
                        .setHost($host, citem.alias)
                        .setAlias(citem.alias)
                        .setEvents(xui.checkEvents(citem.events))
                        .setProperties(citem.properties)
                    if ($host) {
                        $host[citem.alias] = cchild
                    }
                    customComponents.push(citem);
                    ;
                }
            });

            components = xui._addChild(customComponents, child, host, $host);
            xui.each(apis, function (api) {
                if (api.getAutoRun()) {
                    api.invoke();
                }
            })

            xui.each(host.modules, function (moduleConfig) {
                xui._addChildModule(moduleConfig.item, moduleConfig.parent, host, $host);
            });

            return components;
        };
        xui._addChild = function (children, child, host, $host) {
            var components = [], modules = [];
            if (children) {
                xui.each(children, function (citem) {
                    if (citem && citem.alias != 'pagectx') {
                        var cchild;
                        if (citem.key == 'xui.Module' && citem.className) {
                            var module = {
                                item: citem,
                                parent: child
                            }
                            host.modules.push(module);
                        } else {
                            cchild = xui.create(citem.key)
                                .setHost(host, citem.alias)
                                .setAlias(citem.alias)
                                .setEvents(xui.checkEvents(citem.events))
                                .setProperties(citem.properties || citem.iniProp)
                            if ($host) {
                                $host[citem.alias] = cchild
                            }
                            ;

                            if (citem.CS) {
                                cchild.setCustomStyle(citem.CS)
                            }
                            if (child.append) {
                                var target = citem.target || citem.tagter;
                                if (target) {
                                    child.append(cchild, target);
                                } else {
                                    child.append(cchild);
                                }
                            }
                            components.push(cchild);
                            var children = citem.children || citem.sub;
                            if (children) {
                                xui._addChild(children, cchild, host, $host);
                            }
                        }
                    }
                })
            }
            return components;
        };


        xui.addRootComponent = function (properties, host) {
            var child = xui.create(item.key)
                .setHost(host, item.alias)
                .setAlias(item.alias)
                .setEvents(xui.checkEvents(item.events))
                .setProperties(item.properties);


        };
        //检查模块函数转换
        xui.checkEvents = function (events) {

            var nEvent = {};
            if (events && xui.isHash(events)) {
                xui.each(events, function (event, eventKey) {

                    if (event.actions && xui.isArr(event.actions)) {
                        var arrEvent = [];
                        xui.each(event.actions, function (action) {
                            if (action.script) {
                                arrEvent.push(action.script)
                            } else {
                                arrEvent.push(action)
                            }
                        });
                        nEvent[eventKey] = event;
                        nEvent[eventKey].actions = arrEvent;
                    } else {
                        nEvent[eventKey] = event;
                    }
                })
            }

            return nEvent;
        };


        //检查模块函数转换
        xui.checkFunction = function (functions) {
            var nfunctions = [];
            xui.each(functions, function (modulefunction) {
                if (modulefunction.actions) {
                    var iactions = [];
                    xui.each(modulefunction.actions, function (action) {
                        if (action.script) {
                            iactions.push(action.script)
                        } else {
                            iactions.push(action)
                        }
                    });
                    modulefunction.actions = iactions;
                }
                nfunctions.push(modulefunction);
            })

            return nfunctions;
        };


        xui.openDebugWin = function (url, projectName) {
            if (url && projectName) {
                xui.execExpression('$ESD.open()', {url: url, projectName: projectName}, false);
            }
        };
        xui.openOtherWin = function (url, projectName) {
            if (url) {
                xui.execExpression('$ESD.openOtherWin()', {url: url, projectName: projectName}, false);
            }
        };


        xui.intModuleProperties = function (properties, host) {
            var children = [];
            append = function (child) {
                children.push(child.get(0));
            };
            xui.each(properties, function (item) {
                var child;
                if (item.key == 'xui.Module' && item.className) {
                    child = xui.create(item.className, item.key)
                        .setHost(host, item.alias)
                        .setAlias(item.alias)
                        .setEvents(xui.checkEvents(item.events))
                        .setProperties(item.properties)
                } else {
                    child = xui.create(item.key)
                        .setHost(host, item.alias)
                        .setAlias(item.alias)
                        .setEvents(xui.checkEvents(item.events))
                        .setProperties(item.properties)
                }

                if (item.CS) {
                    child.setCustomStyle(item.CS)
                }
                ;
                xui.addChild(item, child, host);
                children.push(child.get(0));
                //children.push(child);
            });
            return children;
        };


        //compitable
        xui.getCom = xui.getModule;
        xui.newCom = xui.newModule;
        xui.showCom = xui.showModule;

        ns.setCom = ns.setModule;
        ns.getComFromCache = ns.getModuleFromCache;
        ns.getCom = ns.getModule;
        ns.newCom = ns.newModule;
        ns.storeCom = ns.storeModule;
        ns.prepareComs = ns.prepareModules;

        xui.ComFactory = ns;
    },
    Static: {
        _pro: {},
        _cache: {},
        _domId: 'xui:ModuleFactory:',
        getProfile: function (key) {
            return key ? this._pro[key] : this._pro;
        },
        setProfile: function (key, value) {
            if (typeof key == 'string')
                this._pro[key] = value;
            else if (xui.isHash(key))
                this._pro = key;
            return this;
        },
        destroyAll: function () {
            xui.each(this._cache, function (o) {
                xui.tryF(o.destroy, [], o);
            });
            this._cache = {};
        },
        broadcast: function (fun) {
            if (typeof fun == 'function') {
                var i, c = this._cache;
                for (i in c)
                    fun.call(c[i], i);
            }
        },

        setModule: function (id, obj) {
            this._cache[id] = obj;
            if (obj) obj.moduleRefId = id;
            return this;
        },
        getModuleFromCache: function (id) {
            return this._cache[id] || null;
        },
        //cached:false->don't get it from cache, and don't cache the result.
        getModule: function (id, onEnd, threadid, cached, properties, events) {
            if (!id) {
                var e = new Error("No id");
                xui.tryF(onEnd, [e, null, threadid]);
                xui.Thread.abort(threadid);
                throw e;
                return;
            }
            cached = cached !== false;
            var c = this._cache,
                p = this._pro,
                config,
                clsPath;

            if (cached && c[id] && !c[id].destroyed) {
                xui.tryF(onEnd, [null, c[id], threadid], c[id]);
                return c[id];
            } else {
                // if no configure
                if (!(config = p[id])) {
                    config = {
                        cls: id,
                        cached: cached,
                        properties: properties,
                        events: events
                    };
                    clsPath = id;
                } else
                    clsPath = config.cls || config;

                var self = arguments.callee,
                    me = this,
                    task = function (cls, config, threadid) {
                        if (!xui.isFun(cls))
                            throw "'" + clsPath + "' is not a constructor";
                        var o = new cls();

                        if (config.properties)
                            xui.merge(o.properties, config.properties, 'all');
                        if (config.events)
                            xui.merge(o.events, config.events, 'all');
                        if (config.cached !== false)
                            xui.ModuleFactory.setModule(id, o);

                        var args = [function (err, module, threadid) {
                            var arr = module.getUIComponents().get(),
                                fun = function (arr, subcfg, firstlayer) {
                                    var self1 = arguments.callee;
                                    xui.arr.each(arr, function (v, i) {
                                        if (v.children) {
                                            var a = [];
                                            xui.arr.each(v.children, function (o) {
                                                a[a.length] = o[0];
                                            });
                                            self1(a, subcfg);
                                        }
                                    });
                                };
                            //handle tag sub from module
                            fun(arr, config.children, 1);
                        }];
                        args.push(threadid || null);

                        //insert first
                        if (onEnd)
                            xui.Thread.insert(threadid, {
                                task: onEnd,
                                args: [null, o, threadid],
                                scope: o
                            });
                        //latter
                        xui.tryF(o[config.iniMethod || 'create'], args, o);
                    };
                xui.Thread.observableRun(function (threadid) {
                    var f = function (threadid) {
                        // this for js path doesn't match Class name space
                        var cls = this || xui.SC.get(clsPath);
                        // it must be a xui Class
                        if (cls && cls.$xui$) {
                            xui.Thread.insert(threadid, {
                                task: task,
                                args: [cls, config, threadid]
                            });
                        } else {
                            var e = new Error("Cant find Class '" + clsPath + "' in the corresponding file (maybe SyntaxError)");
                            xui.tryF(onEnd, [e, null, threadid]);
                            xui.Thread.abort(threadid);
                            throw e;
                        }
                    };
                    xui.SC(clsPath, function (path) {
                        if (path)
                            f.call(this, threadid);
                        else {
                            var e = new Error("No class name");
                            xui.tryF(onEnd, [e, null, threadid]);
                            xui.Thread.abort(threadid);
                            throw e;
                        }
                    }, true, threadid, {
                        retry: 0,
                        onFail: function (e) {
                            xui.tryF(onEnd, [e, null, threadid]);
                        }
                    });
                }, null, threadid);
            }
        },
        newModule: function (cls, onEnd, threadid, properties, events) {
            return this.getModule(cls, onEnd, threadid, false, properties, events);
        },
        storeModule: function (id) {
            var m, t, c = this._cache, domId = this._domId;
            if (t = c[id]) {
                if (!(m = xui.Dom.byId(domId)))
                //using display:none here for performance, when appendchild, it'll not trigger layout etc.
                    xui('body').prepend(xui.create('<div id="' + domId + '" style="display:none;"></div>'));
                m = xui(domId);
                t = t.getUIComponents();
                if (!t.isEmpty()) {
                    //detach
                    t.get(0).unlinkParent();
                    //move to hide
                    m.append(t);
                }
            }
        },
        prepareModules: function (arr) {
            var self = this, funs = [];
            xui.arr.each(arr, function (i) {
                funs.push(function () {
                    self.getModule(i);
                });
            });
            xui.Thread(null, funs, 500).start();
            return this;
        }
    }
});